package com.xixing.weixin.cmd;

import com.xixing.weixin.exception.WeixinExecuteException;
import com.xixing.weixin.domain.Weixin;
import com.xixing.weixin.response.Response;
import com.xixing.weixin.response.TextResponse;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 命令工厂.
 */
public class CommandFactory {

    private static ResourceBundle resourceBundle = ResourceBundle.getBundle("command");

    private Map<String, AbstractExecutor> commandMap = new HashMap<String, AbstractExecutor>();
    private Map<String, Command> userCommand = new HashMap<String, Command>();
    private Command helpCommand = new Command(new HelpExecutor());
    private AbstractExecutor loginCommand;

    private int timeout;

    public CommandFactory() {
        timeout = 4800;
    }

    public void setCommands(List<AbstractExecutor> commands) {
        for (AbstractExecutor command : commands) {
            commandMap.put(command.getCommandName(), command);
            for (String alias : command.getAlias()) {
                commandMap.put(AbstractExecutor.normalCommand(alias), command);
            }

            if(command.getClass() == LoginExecutor.class) {
                loginCommand = command;
            }
        }
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    // 执行命令.
    public Response execute(final Weixin weixin) {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        FutureTask<Response> future =
                new FutureTask<Response>(new Callable<Response>() { //使用Callable接口作为构造参数.
                    public Response call() {
                        Command command = createCommand(weixin);
                        try {
                            Response execute = command.execute(CommandFactory.this, weixin);
                            return execute;
                        } catch (WeixinExecuteException e) {
                            return TextResponse.create(weixin, e.getMessage());
                        }

                    }
                });
        executor.execute(future);


        try {
            //取得结果，同时设置超时执行时间为5秒.
            return future.get(timeout, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
            future.cancel(true);
        } catch (ExecutionException e) {
            future.cancel(true);
        } catch (TimeoutException e) {
            future.cancel(true);
        } finally {
            executor.shutdown();
        }

        return TextResponse.create(weixin, resourceBundle.getString("CommandFactory.timeout"));

    }



    //创建命令.
    public Command createCommand(Weixin weixin) {
        // 取得指令信息.
        String content = weixin.getContent();


        if (content != null) {

            // 是否为直接命令.
            String[] command = AbstractExecutor.splitCommand(content);
            AbstractExecutor cmd = findCommand(command[0]);
            if (cmd != null) {
                Command commandWrap = new Command(cmd);
                userCommand.put(weixin.getFromUserName(), commandWrap);
                return commandWrap;
            }
        }

        // 取得上一个命令.
        Command cmd = getCurrentCommand(weixin.getFromUserName());
        if (cmd == null) return helpCommand;
        return cmd;
    }


    /**
     * 根据命令名取得命令.
     *
     * @param commandName 命令名.
     * @return 命令或者NULL.
     */
    public AbstractExecutor findCommand(String commandName) {
        return commandMap.get(AbstractExecutor.normalCommand(commandName));
    }

    public Command getCurrentCommand(String userName) {
        return userCommand.get(userName);
    }

    public AbstractExecutor getLoginCommand() {
        return loginCommand;
    }
}
